﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using CPAScoringPerformanceTester.TestTask;
using System.Threading;
using System.Diagnostics;
using System.Windows.Media;
using System.ServiceModel;
using ATA.OLSD.ScoringService.Entities;

namespace CPAScoringPerformanceTester.Models
{
    public enum RobotStatus
    {
        Pending,
        Setting,
        Running,
        Stopped,
        Completed,
        Error
    }

    public class Robot : INotifyPropertyChanged
    {
        #region Fields
        private bool stop;
        private int repeatCount;
        private TestTaskItem type;
        private TestTaskBase task;
        private TaskThreader worker = new TaskThreader();
        private UserAccessTicket ticket;
        private PersonTask scoreTask;
        private ScoringItem[] paperItems;

        public event PropertyChangedEventHandler PropertyChanged;
        public event RobotStatusChangedEventHandler RobotStatusChanged;
        public event ExecutionCompletedEventHandler ExecutionCompleted;
        #endregion

        #region Properties
        #region Username&Password
        private string username;
        public string Username
        {
            get { return username; }
            set
            {
                if (value == username) return;
                username = value;
                OnPropertyChanged("Username");
            }
        }

        private string password;
        public string Password
        {
            get { return password; }
            set
            {
                if (value == password) return;
                password = value;
                OnPropertyChanged("Password");
            }
        }
        #endregion

        #region Status
        private RobotStatus status;
        public RobotStatus Status
        {
            get { return status; }
            set
            {
                status = value;
                switch (status)
                {
                    case RobotStatus.Setting:
                        StatusDesc = "Setting up test data";
                        break;
                    default:
                        StatusDesc = status.ToString();
                        break;
                }
                OnPropertyChanged("Status");
                OnPropertyChanged("StatusDesc");
            }
        }
        public string StatusDesc { get; set; }
        #endregion

        #region Task Name
        private string taskName;
        public string TaskName
        {
            get { return taskName; }
            set
            {
                taskName = value;
                OnPropertyChanged("TaskName");
            }
        }
        #endregion

        #region Success&Failure
        private int successCount;
        public int SuccessCount
        {
            get { return successCount; }
            set
            {
                successCount = value;
                OnPropertyChanged("SuccessCount");
                OnPropertyChanged("SuccessRate");
                OnPropertyChanged("FailureRate");
                OnExecutionCompleted(true);
            }
        }

        private int failureCount;
        public int FailureCount
        {
            get { return failureCount; }
            set
            {
                failureCount = value;
                OnPropertyChanged("FailureCount");
                OnPropertyChanged("FailureRate");
                OnPropertyChanged("SuccessRate");
                OnExecutionCompleted(false);
            }
        }

        private int totalCount;
        public int TotalCount
        {
            get { return totalCount; }
            set
            {
                totalCount = value;
                OnPropertyChanged("TotalCount");
            }
        }

        public decimal SuccessRate
        {
            get
            {
                if (TotalCount == 0) return 0;
                return (decimal)SuccessCount / TotalCount;
            }
        }

        public decimal FailureRate
        {
            get
            {
                if (TotalCount == 0) return 0;
                return (decimal)FailureCount / TotalCount;
            }
        }
        #endregion

        #region Average Time
        private long totalTime = 0;
        private long averageTime;
        public long AverageTime
        {
            get { return averageTime; }
            set
            {
                if (averageTime == value) return;
                averageTime = value;
                OnPropertyChanged("AverageTime");
            }
        }

        #endregion

        #region ColorSign
        private SolidColorBrush colorSign;
        public SolidColorBrush ColorSign
        {
            get { return colorSign; }
            set
            {
                colorSign = value;
                OnPropertyChanged("ColorSign");
            }
        }
        #endregion

        #region Execution Information
        private string information;
        public string Information
        {
            get { return information; }
            set
            {
                if (value == information) return;
                information = value;
                OnPropertyChanged("Information");
            }
        }
        #endregion

        #endregion

        public Robot()
        {
            ColorSign = new SolidColorBrush(GetRandomColor());
        }

        public void SetTask(TestTaskItem taskType)
        {
            this.type = taskType;
            switch (type)
            {
                case TestTaskItem.ServiceConnection:
                    this.task = new ServiceConnectionTask();
                    this.TaskName = ServiceConnectionTask.TaskName;
                    break;
                case TestTaskItem.UserLogin:
                    this.task = new UserLoginTask(this.Username, this.Password);
                    this.TaskName = UserLoginTask.TaskName;
                    break;
                case TestTaskItem.ScoreTaskList:
                    this.task = new ScoreTaskListTask(ticket);
                    this.TaskName = ScoreTaskListTask.TaskName;
                    break;
                case TestTaskItem.ScoreTaskDetail:
                    this.task = new ScoreTaskDetailTask(ticket);
                    this.TaskName = ScoreTaskDetailTask.TaskName;
                    break;
                case TestTaskItem.ScoreTaskPaperItems:
                    this.task = new ScoreTaskPaperItemsTask(ticket);
                    this.TaskName = ScoreTaskPaperItemsTask.TaskName;
                    break;
                case TestTaskItem.ScoreTaskApply:
                    this.task = new ScoreTaskApplyTask(ticket, scoreTask, paperItems);
                    this.TaskName = ScoreTaskApplyTask.TaskName;
                    break;
                case TestTaskItem.ScoreTaskSave:
                    this.task = new ScoreTaskSaveTask(ticket, scoreTask);
                    this.TaskName = ScoreTaskSaveTask.TaskName;
                    break;
                case TestTaskItem.ScoreTaskSubmit:
                    this.task = new ScoreTaskSubmitTask(ticket);
                    this.TaskName = ScoreTaskSubmitTask.TaskName;
                    break;
            }

            OnPropertyChanged("TaskName");

            Reset();
        }

        public void SetUp()
        {
            stop = false;
            OnRobotStatusChanged(RobotStatus.Setting);
            task.Setup();
            if (task.IsError)
            {
                Information = task.Message;
                task.CleanUp();
                OnRobotStatusChanged(RobotStatus.Error);
            }
        }

        public void Run()
        {
            if (stop)
            {
                task.CleanUp();
                OnRobotStatusChanged(RobotStatus.Stopped);
                return;
            }

            switch (type)
            {
                case TestTaskItem.ServiceConnection:
                    repeatCount = ServiceConnectionSettings.Instance.RepeatCount;
                    break;
                case TestTaskItem.UserLogin:
                    repeatCount = UserLoginSettings.Instance.RepeatCount;
                    break;
                case TestTaskItem.ScoreTaskList:
                    repeatCount = ScoreTaskListSettings.Instance.RepeatCount;
                    break;
                case TestTaskItem.ScoreTaskDetail:
                    repeatCount = ScoreTaskDetailSettings.Instance.RepeatCount;
                    break;
                case TestTaskItem.ScoreTaskPaperItems:
                    repeatCount = ScoreTaskPaperItemsSettings.Instance.RepeatCount;
                    break;
                case TestTaskItem.ScoreTaskApply:
                    repeatCount = ScoreTaskApplySettings.Instance.RepeatCount;
                    break;
                case TestTaskItem.ScoreTaskRecord:
                    repeatCount = ScoreTaskRecordSettings.Instance.RepeatCount;
                    break;
                case TestTaskItem.ScoreTaskSave:
                    repeatCount = ScoreTaskSaveSettings.Instance.RepeatCount;
                    break;
                case TestTaskItem.ScoreTaskSubmit:
                    repeatCount = ScoreTaskSubmitSettings.Instance.RepeatCount;
                    break;
            }

            if (worker != null)
            {
                stop = false;
                worker.Start(DoWork);
            }
        }

        public void Stop()
        {
            stop = true;

            if (this.Status == RobotStatus.Completed)
            {
                OnRobotStatusChanged(RobotStatus.Stopped);
            }
        }

        public void Reset()
        {
            stop = false;
            TotalCount = 0;
            SuccessCount = 0;
            FailureCount = 0;
            totalTime = 0;
            AverageTime = 0;
            Information = string.Empty;
            OnRobotStatusChanged(RobotStatus.Pending);
        }

        private void DoWork()
        {
            if (task.IsError) return;

            OnRobotStatusChanged(RobotStatus.Running);
            while (repeatCount == 0 || TotalCount < repeatCount)
            {
                Stopwatch watcher = new Stopwatch();
                watcher.Start();
                bool result = task.Execute();
                watcher.Stop();

                if (stop)
                {
                    task.CleanUp();
                    OnRobotStatusChanged(RobotStatus.Stopped);
                    return;
                }

                TotalCount++;
                if (result)
                {
                    SuccessCount++;
                    totalTime += watcher.ElapsedMilliseconds;
                    AverageTime = totalTime / SuccessCount;
                    Information = "Execute Successfully";

                    switch (type)
                    {
                        case TestTaskItem.UserLogin:
                            this.ticket = ((UserLoginTask)task).Ticket;
                            break;
                        case TestTaskItem.ScoreTaskDetail:
                            this.scoreTask = ((ScoreTaskDetailTask)task).Task;
                            break;
                        case TestTaskItem.ScoreTaskPaperItems:
                            this.paperItems = ((ScoreTaskPaperItemsTask)task).Items;
                            break;
                        case TestTaskItem.ScoreTaskSubmit:
                            scoreTask = null;
                            break;
                    }
                }
                else
                {
                    FailureCount++;
                    Information = task.Message;
                }
            }

            task.CleanUp();
            OnRobotStatusChanged(RobotStatus.Completed);
        }

        private void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        private void OnRobotStatusChanged(RobotStatus status)
        {
            this.Status = status;
            if (RobotStatusChanged != null)
            {
                RobotStatusChanged(this, new RobotStatusChangedEventArgs(status));
            }
        }

        private void OnExecutionCompleted(bool success)
        {
            if (ExecutionCompleted != null)
            {
                ExecutionCompleted(this, new ExecutionCompletedEventArgs(success));
            }
        }

        private Color GetRandomColor()
        { 
            Random random = new Random(Guid.NewGuid().GetHashCode());
            byte r = Convert.ToByte(random.Next(0, 255));
            byte g = Convert.ToByte(random.Next(0, 255));
            byte b = Convert.ToByte(random.Next(0, 255));
            return Color.FromRgb(r, g, b);
        }
    }
}
